#include <stdio.h>		/* for NULL and printf */

#include "hoc.h"		/* for Symbol_t, Inst_t, Datum_t and execerror */
#include "code.h"		/* for Datum_t and Inst_t */
#include "y.tab.h"		/* for symbols */
#include "math.h"		/* for Pow */

/* Configure Virtual Machine */

#define NSTACK	256			/* Data Stack Size */
Datum_t	stack[NSTACK];		/* The Stack */
Datum_t	*ptr_stack;			/* Next free spot on stack (stack pointer)*/

#define NPROG	2000		/* Program Space */
Inst_t	prog[NPROG];		/* The Machine Memory */
Inst_t	*ptr_base = prog;	/* Start of code space */
Inst_t	*ptr_prog;			/* Next free spot for code generation */
Inst_t	*ptr_local;			/* Program Locale (Counter) */

/* Return (or call) Stack */

typedef struct Frame_s {		/* Proc/Func call stack frame */
	Symbol_t	*ptr_sym;		/* Symbol Table Entry */
	Inst_t		*ptr_return;	/* Locale to return to */
	Datum_t		*argn;			/* n-th argument on stack */
	int			iargs;			/* number of arguments */
} Frame_t;

#define	NFRAME	100				/* Size of Call stack */
Frame_t		frame[NFRAME];		/* Call Stack */
Frame_t		*ptr_frame;			/* Call Stack Pointer */

int		returning;				/* True if statement seen */

/* Trace mode */

#include "dump.h"		/* for Trace mode support functions */
int trace_mode;			/* Non-Zero = Trace mode */
int trace_indent;		/* Indent level for Trace mode */
int trace_nostack;		/* 0 = No stack display for this op-code */


/* Init for code generation */
void initcode() {
	ptr_prog = ptr_base;	/* Start from recent code space */
	ptr_stack = stack;		/* Clear Data Stack */
	ptr_frame = frame;		/* Clear Return Stack */
	returning = 0;			/* Outside of function */

	trace_mode = 0;			/* Clear Trace mode */
	trace_indent = 0;		/* No Trace indent */
	trace_nostack = 1;		/* Enable Stack display */
}

/* Data Stack Handling */

/* Push Data onto Stack */
void push( Datum_t dval ) {
	if ( ptr_stack >= &stack[NSTACK] ) {
		execerror("Stack Overflow", NULL);
	}
	*(ptr_stack++) = dval;
}

/* Pop and return elem from stack */
Datum_t pop() {
	if ( ptr_stack <= stack ) {
		execerror("Stack Underflow", NULL);
	}
	return *(--ptr_stack);
}

/* Drop Top Of Stack (TOS) */
void drop() {
	pop();
}

/* Push constant onto stack */
void constpush() {
	Datum_t tos;
	Symbol_t* sym = (Symbol_t *)*(ptr_local++);
	tos.datatype = sym->datatype;
	switch(tos.datatype)
	{
	case INT:
		/*printf("constpush INT\n");*/
		tos.u.ival = sym->u.ival;
		/*printf("ival set to %d dval %g\n",tos.u.ival,tos.u.dval);*/
		break;
	case CHAR:
		tos.u.cval = sym->u.cval;
		break;
	case BOOL:
		/*printf("constpush BOOL\n");*/
		tos.u.bval = sym->u.bval;
		break;
	case FLOAT:
	default:
		/*printf("constpush FLOAT\n");*/
		tos.u.dval = sym->u.dval;
		/*printf("ival set to %d dval %g\n",tos.u.ival,tos.u.dval);*/
		break;
	}
	push(tos);
}

/* Push variable onto stack */
void varpush() {
	Datum_t tos;
	Symbol_t *ptr_sym = (Symbol_t *)(*(ptr_local++));	
	tos.u.ptr_sym = ptr_sym;
	tos.datatype = ptr_sym->datatype;
	push(tos);
}

/* Call Stack Handling */

/* Call a function / procedure */
void call() {
	Symbol_t *ptr_sym = (Symbol_t *)(*(ptr_local++));

	if ( trace_mode ) {
		printf("%s()\n", ptr_sym->ptr_cname);
	}

	if ( ptr_frame++ >= &frame[NFRAME - 1] ) {
		execerror(ptr_sym->ptr_cname, "call nested too deeply");
	}

	ptr_frame->ptr_sym = ptr_sym;
	ptr_frame->iargs = (int)*(ptr_local++);

	ptr_frame->ptr_return = (Inst_t *)(ptr_local);
	ptr_frame->argn = ptr_stack - 1;

	execute((Inst_t *)ptr_sym->u.defn);

	returning = 0;

	if ( trace_mode ) {
		trace_nostack = 0;
	}
}

/* Common "Return" code */
void hocret() {
	int i;

	/* Clean up the data stack */
	for ( i = 0; i < ptr_frame->iargs; i++ ) {
		drop();
	}

	ptr_local = (Inst_t *)(ptr_frame--)->ptr_return;
	returning = 1;
}

/* Return from a function */
void funcret() {
	Datum_t tos;
	if ( ptr_frame->ptr_sym->itype == PROCEDURE ) {
		execerror(ptr_frame->ptr_sym->ptr_cname,
				"Procedure returns a value");
	}
	tos = pop();
	hocret();
	push(tos);
}

/* Return from a procedure */
void procret() {
	if ( ptr_frame->ptr_sym->itype == FUNCTION ) {
		execerror(ptr_frame->ptr_sym->ptr_cname,
				"Function returns no value");
	}
	hocret();
}

/* Return pointer to argument */
double *getarg() {
	int iargs = (int) *(ptr_local++);
	if ( iargs > ptr_frame->iargs ) {
		execerror(ptr_frame->ptr_sym->ptr_cname,
			"not enough arguments");
	}
	/* Assume arguments are always of type double */
	return &ptr_frame->argn[iargs - ptr_frame->iargs].u.dval;
}

/* Push argument onto stack */
void arg() {
	Datum_t tos;
	/* Assume arguments are always of type double */
	tos.u.dval = *getarg();
	push(tos);
}

/* Store TOS in argument */
void argassign() {
	Datum_t tos;
	tos = pop();
	push(tos);
	/* Assume arguments are always of type double */
	*getarg() = tos.u.dval;
}


/* Internal Operation of the Virtual Machine */

/* Install one instruction or operand */
Inst_t *code(Inst_t func) {
	Inst_t* ptr_oldprog = ptr_prog;

	if ( ptr_prog >= &prog[NPROG] ) {
		execerror("Program too big", NULL);
	}
	*(ptr_prog++) = func;

	return ptr_oldprog;
}

/* Run the machine */
void execute(Inst_t *ptr_prog) {
	ptr_local = ptr_prog;
	if ( trace_mode ) {
		/* We are in trace mode */
		++trace_indent;
		while ( *ptr_local != STOP && !returning ) {
			printopcode();			/* Display line number and op-code */
			(*(ptr_local++))();		/* Execute the op-code */
			if ( trace_nostack ) {
				printstack();		/* Display the current stack */
				printf("\n");
			}
			trace_nostack = 1;		/* Reenable Stack Display */
		}
		--trace_indent;
	} else {
		while ( *ptr_local != STOP && !returning ) {
			(*(ptr_local++))();
		}
	}
}

/* Put func/proc in symbol table */
void define(Symbol_t *ptr_sym) {
	ptr_sym->u.defn = ptr_base;	/* Start of code */
	ptr_base = ptr_prog;
}

bool validcondition(Datum_t tos)
{
	if((tos.datatype == FLOAT && tos.u.dval) ||
	   (tos.datatype == INT && tos.u.ival) ||
	   (tos.datatype == BOOL && tos.u.bval) ||
	   (tos.datatype == CHAR && tos.u.cval))
	{
		printf("Return TRUE\n");
		getchar();	
		return true;
	}
	else
	{
		printf("Return FALSE\n");
		getchar();	
		return false;
	}

}

/* Operations for the Virtual Machine */
/* TOS: Top Of Stack */
/* NOS: Next On Stack */

/* While <condition> Do <loop-code> end <next> */
/* PC[0] = <loop-code> */
/* PC[1] = <next> */
/* PC[2] = <condition> */

void whilecode() {
	Datum_t tos;
	Inst_t *ptr_code = ptr_local;

	if ( trace_mode ) {
		printstack();
		printf("\n");
		printLN(-1, trace_indent);
		printf("Test condition:\n");
	}
	execute(ptr_code + 2);						/* Condition */
	tos = pop();
	/* Assume while condition true remains in dval */
	while ( validcondition(tos) ) {
		if ( trace_mode ) {
			printLN(-1, trace_indent);
			printf("Loop Body:\n");
		}
		execute(*((Inst_t **)(ptr_code)));	/* Loop-code */
		if ( returning ) {					/* Return statment found? */
			break;							/* Yes => Stop the loop */
		}
		if ( trace_mode ) {
			printLN(-1, trace_indent);
			printf("Test condition:\n");
		}
		execute(ptr_code + 2);				/* Condition */
		tos = pop();
	}
	if ( ! returning ) {
		ptr_local = *((Inst_t **)(ptr_code + 1));	/* Next Statement */
	}
	if ( trace_mode ) {
		printLN(-1, trace_indent);
		printf("end-while\n");
		trace_nostack = 0;				/* Suppress Stack Display */
	}
}

/* If <condition> then <true-code> else <false-code> end-if <next> */
/* PC[0] = <true-code> */
/* PC[1] = <false-code> */
/* PC[2] = <next> */
/* PC[3] = <condition> */

void ifcode() {
	Datum_t tos;
	Inst_t *ptr_code = ptr_local;

	if ( trace_mode ) {
		printstack();
		printf("\n");
		printLN(-1, trace_indent);
		printf("Test condition:\n");
	}
	execute(ptr_code + 3);						/* Condition */
	tos = pop();
	/* Assume if condition remains in dval */
	if ( validcondition(tos) ) {							/* Test Condition */
		if ( trace_mode ) {
			printLN(-1, trace_indent);
			printf("True Part:\n");
		}
		execute(*((Inst_t **)(ptr_code)));		/* True Part */
	} else {
		if ( trace_mode ) {
			printLN(-1, trace_indent);
			printf("False Part:\n");
		}
		if ( *(Inst_t **)(ptr_code + 1) ) {
			execute(*(Inst_t **)(ptr_code + 1));	/* False Part */
		}
	}
	if ( ! returning ) {
		ptr_local = *((Inst_t **)(ptr_code + 2));		/* Next Stmt */
	}
	if ( trace_mode ) {
		printLN(-1, trace_indent);
		printf("end-if\n");
		trace_nostack = 0;				/* Suppress stack trace */
	}
}

/* Evaluate built-in on top of stack */
void bltin() {
	Datum_t tos;
	tos = pop();
	if(tos.datatype == FLOAT)
	{
		tos.u.dval = (*(double (*)(double))*(ptr_local++))(tos.u.dval);
	}
	else if(tos.datatype == INT)
	{
		tos.u.ival = (*(double (*)(double))*(ptr_local++))(tos.u.ival);
	}
	else
	{
		printf("Attempted operation on invalid type\n");
	}
	push(tos);
}

/* Evaluate variable on stack */
void eval() {
	Datum_t tos;
	tos = pop();
	if (tos.u.ptr_sym->itype == UNDEF) {
		execerror("Undefined Variable", tos.u.ptr_sym->ptr_cname);
	}
	/* Check types here and copy required type */
	switch(tos.u.ptr_sym->datatype)
	{
	case INT:
		/*printf("Eval int\n");	*/
		tos.u.ival = tos.u.ptr_sym->u.ival;
		break;
	case CHAR:
		tos.u.cval = tos.u.ptr_sym->u.cval;
		break;
	case BOOL:
		/*printf("Eval BOOL\n");*/
		tos.u.bval = tos.u.ptr_sym->u.bval;
		break;
	case FLOAT:
	default:
		/*printf("Eval float\n");*/
		tos.u.dval = tos.u.ptr_sym->u.dval;
		break;
	}
	

	/*tos.u.dval = tos.u.ptr_sym->u.dval;*/
	push(tos);
}

/* Add top two elems on stack */
void hoc_add() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival += nos.u.ival;
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval += nos.u.dval;
	}
	else if(tos.datatype == FLOAT && nos.datatype == INT)
	{
		printf("Warning: implicit cast of float to int\n");
		tos.u.ival = (int)tos.u.dval + nos.u.ival;
		tos.datatype = INT;
	}
	else if(tos.datatype == INT && nos.datatype == FLOAT)
	{
		printf("Warning: implicit cast of int to float\n");
		tos.u.dval = (float)tos.u.ival + nos.u.dval;
		tos.datatype = FLOAT;
	}	
	else
	{
		printf("Attempted addition of invalid types\n");
	}
	push(tos);
}

/* Subtract TOS from NOS */
void hoc_sub() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	if(nos.datatype == INT && tos.datatype == INT)
	{
		nos.u.ival -= tos.u.ival;
	}
	else if(nos.datatype == FLOAT && tos.datatype == FLOAT)
	{
		nos.u.dval -= tos.u.dval;
	}
	else if(nos.datatype == FLOAT && tos.datatype == INT)
	{
		printf("Warning: implicit cast of float to int\n");
		nos.u.ival = (int)nos.u.dval - tos.u.ival;
		nos.datatype = INT;
	}
	else if(nos.datatype == INT && tos.datatype == FLOAT)
	{
		printf("Warning: implicit cast of int to float\n");
		nos.u.dval = (float)nos.u.ival - tos.u.dval;
		nos.datatype = FLOAT;
	}	
	else
	{
		printf("Attempted subtraction of invalid types\n");
	}
	push(nos);
}

/* Multiply TOS by NOS */
void hoc_mul() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
		if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival *= nos.u.ival;
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval *= nos.u.dval;
	}
	else if(tos.datatype == FLOAT && nos.datatype == INT)
	{
		printf("Warning: implicit cast of float to int\n");
		tos.u.ival = (int)tos.u.dval * nos.u.ival;
		tos.datatype = INT;
	}
	else if(tos.datatype == INT && nos.datatype == FLOAT)
	{
		printf("Warning: implicit cast of int to float\n");
		tos.u.dval = (float)tos.u.ival * nos.u.dval;
		tos.datatype = FLOAT;
	}	
	else
	{
		printf("Attempted multiplication of invalid types\n");
	}
	push(tos);
}

/* Divide NOS by TOS */
void hoc_div() {
	Datum_t tos, nos;
	tos = pop();
	if ( (tos.datatype == FLOAT && tos.u.dval == 0.0) || (tos.datatype == INT && tos.u.ival == 0 )) {
		execerror("Division by Zero", NULL);
	}
	nos = pop();
	if(nos.datatype == INT && tos.datatype == INT)
	{
		nos.u.ival /= tos.u.ival;
	}
	else if(nos.datatype == FLOAT && tos.datatype == FLOAT)
	{
		nos.u.dval /= tos.u.dval;
	}
	else if(nos.datatype == FLOAT && tos.datatype == INT)
	{
		printf("Warning: implicit cast of float to int\n");
		nos.u.ival = (int)nos.u.dval / tos.u.ival;
		nos.datatype = INT;
	}
	else if(nos.datatype == INT && tos.datatype == FLOAT)
	{
		printf("Warning: implicit cast of int to float\n");
		nos.u.dval = (float)nos.u.ival / tos.u.dval;
		nos.datatype = FLOAT;
	}	
	else
	{
		printf("Attempted division of invalid types\n");
	}
	push(nos);
}

/* TOS = NOS mod TOS */
void hoc_mod() {
	Datum_t tos, nos;
	tos = pop();
	if ( tos.u.dval == 0.0 ) {
		execerror("Division by Zero", NULL);
	}
	nos = pop();
	/* Add type checking to remove the explicit long casts here */
	nos.u.dval = (long)nos.u.dval % (long)tos.u.dval;
	push(nos);
}

/* Negate TOS */
void hoc_negate() {
	Datum_t tos;
	tos = pop();
	switch(tos.datatype)
	{
	case INT:
		tos.u.ival = -tos.u.ival;
		break;
	case FLOAT:
		tos.u.dval = -tos.u.dval;
		break;
	default:
		printf("Invalid operation for this type\n");
		break;
	}
	push(tos);
}


/* Raise NOS to the power of TOS */
void hoc_power() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/*tos.u.dval = Pow(nos.u.dval, tos.u.dval);*/	
	if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival = Pow(nos.u.ival,tos.u.ival);
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval = Pow(nos.u.dval, tos.u.dval);
	}
	else if(tos.datatype == FLOAT && nos.datatype == INT)
	{
		tos.u.dval = Pow(tos.u.dval,nos.u.ival);
	}
	else if(tos.datatype == INT && nos.datatype == FLOAT)
	{
		tos.u.ival = Pow(tos.u.ival,nos.u.dval);
	}	
	else
	{
		printf("Attempted power of invalid types\n");
	}
	push(tos);
}

/* TOS grater than NOS */
void hoc_gt() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here - int,flot, char*/
	/* tos.u.dval = (double)(nos.u.dval > tos.u.dval); */
	if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival = nos.u.ival > tos.u.ival;
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval = (double)(nos.u.dval > tos.u.dval);
	}
	else if(tos.datatype == FLOAT && nos.datatype == INT)
	{
		tos.u.dval = (double)(tos.u.dval > nos.u.ival);
	}
	else if(tos.datatype == INT && nos.datatype == FLOAT)
	{
		tos.u.ival = (int)(tos.u.ival > nos.u.dval);
	}	
	else if(tos.datatype == CHAR && nos.datatype == CHAR)
	{
		tos.u.cval = (int)(tos.u.cval > nos.u.cval);
	}
	else
	{
		printf("Attempted greater than operation on invalid types\n");
	}
	push(tos);
}

/* TOS less than NOS */
void hoc_lt() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here - int, float, char*/
	/*tos.u.dval = (double)(nos.u.dval < tos.u.dval);*/
	if(tos.datatype == INT && nos.datatype == INT)
	{
		printf("int, is nos %d less than tos %d\n",nos.u.ival,tos.u.ival);
		tos.u.ival = nos.u.ival < tos.u.ival;
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval = (double)(nos.u.dval < tos.u.dval);
	}
	else if(tos.datatype == FLOAT && nos.datatype == INT)
	{
		tos.u.dval = (double)(tos.u.dval < nos.u.ival);
	}
	else if(tos.datatype == INT && nos.datatype == FLOAT)
	{
		tos.u.ival = (int)(tos.u.ival < nos.u.dval);
	}	
	else if(tos.datatype == CHAR && nos.datatype == CHAR)
	{
		tos.u.cval = (int)(tos.u.cval < nos.u.cval);
	}
	else
	{
		printf("Attempted less than operation on invalid types\n");
	}
	push(tos);
}

/* TOS greater than or equal to NOS */
void hoc_ge() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here, int float char */
	/*tos.u.dval = (double)(nos.u.dval >= tos.u.dval);*/
	if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival = nos.u.ival >= tos.u.ival;
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval = (double)(nos.u.dval >= tos.u.dval);
	}
	else if(tos.datatype == FLOAT && nos.datatype == INT)
	{
		tos.u.dval = (double)(tos.u.dval >= nos.u.ival);
	}
	else if(tos.datatype == INT && nos.datatype == FLOAT)
	{
		tos.u.ival = (int)(tos.u.ival >= nos.u.dval);
	}	
	else if(tos.datatype == CHAR && nos.datatype == CHAR)
	{
		tos.u.cval = (int)(tos.u.cval >= nos.u.cval);
	}
	else
	{
		printf("Attempted greater than or equal to operation on invalid types\n");
	}
	push(tos);
}

/* TOS less than or equal to NOS */
void hoc_le() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here - int float char*/
	/*tos.u.dval = (double)(nos.u.dval <= tos.u.dval);*/
	if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival = nos.u.ival <= tos.u.ival;
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval = (double)(nos.u.dval <= tos.u.dval);
	}
	else if(tos.datatype == FLOAT && nos.datatype == INT)
	{
		tos.u.dval = (double)(tos.u.dval <= nos.u.ival);
	}
	else if(tos.datatype == INT && nos.datatype == FLOAT)
	{
		tos.u.ival = (int)(tos.u.ival <= nos.u.dval);
	}	
	else if(tos.datatype == CHAR && nos.datatype == CHAR)
	{
		tos.u.cval = (int)(tos.u.cval <= nos.u.cval);
	}
	else
	{
		printf("Attempted less than or equal to operation on invalid types\n");
	}
	push(tos);
}

/* TOS equal to NOS */
void hoc_eq() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here - all types */
	/* tos.u.dval = (double)(tos.u.dval == nos.u.dval);*/
	if(tos.datatype == nos.datatype)
	{		
	switch(tos.datatype)
		{
		case INT: tos.u.ival = (int)(tos.u.ival == nos.u.ival); break;
		case CHAR: tos.u.cval = (char)(tos.u.cval == nos.u.cval); break;
		case BOOL: tos.u.bval = (bool)(tos.u.bval == nos.u.bval); break;
		case FLOAT: 
		default:
			tos.u.dval = (double)(tos.u.dval == nos.u.dval); 			break;	
		}
	}
	else	
	{
		printf("Attempted equality operation on invalid types\n");		
	}
	push(tos);
}

/* TOS not equal to NOS */
void hoc_ne() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here - all types*/
	/*tos.u.dval = (double)(tos.u.dval != nos.u.dval);*/
	if(tos.datatype == nos.datatype)
	{		
	switch(tos.datatype)
		{
		case INT: tos.u.ival = (int)(tos.u.ival != nos.u.ival); break;
		case CHAR: tos.u.cval = (char)(tos.u.cval != nos.u.cval); break;
		case BOOL: tos.u.bval = (bool)(tos.u.bval != nos.u.bval); break;
		case FLOAT: 
		default:
			tos.u.dval = (double)(tos.u.dval != nos.u.dval); 			break;	
		}
	}
	else	
	{
		printf("Attempted non-equality operation on invalid types\n");		
	}
	push(tos);
}

/* TOS and NOS */
void hoc_and() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here */
	if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival = (int)(tos.u.ival != 0 && nos.u.ival != 0.0);
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval = (double)(tos.u.dval != 0.0 && nos.u.dval != 0.0);
	}
	else
	{
		printf("Attempted AND operation on invalid types\n");	
	}
	push(tos);
}

/* TOS or NOS */
void hoc_or() {
	Datum_t tos, nos;
	tos = pop();
	nos = pop();
	/* Add type checking here */
	if(tos.datatype == INT && nos.datatype == INT)
	{
		tos.u.ival = (int)(tos.u.ival != 0 || nos.u.ival != 0.0);
	}
	else if(tos.datatype == FLOAT && nos.datatype == FLOAT)
	{
		tos.u.dval = (double)(tos.u.dval != 0.0 || nos.u.dval != 0.0);
	}
	else
	{
		printf("Attempted OR operation on invalid types\n");	
	}
	push(tos);
}

/* Not TOS */
void hoc_not() {
	Datum_t tos;
	tos = pop();
	/* Add type checking here */
	/*tos.u.dval = (double)(tos.u.dval == 0.0);*/
	if(tos.datatype == INT)
	{
		tos.u.ival = (int)(tos.u.ival == 0);
	}
	else if(tos.datatype == FLOAT)
	{
		tos.u.dval = (double)(tos.u.dval == 0.0);
	}
	else
	{
		printf("Attempted NOT operation on invalid type\n");	
	}
	push(tos);
}

/* Assign value (NOS) to variable (TOS) */
void assign() {
	Datum_t tos, nos;
	tos = pop();
	if ( tos.u.ptr_sym->itype != VAR && tos.u.ptr_sym->itype != UNDEF ) {
		execerror("Assignment to Non-Variable", tos.u.ptr_sym->ptr_cname);
	}
	nos = pop();

	if((tos.datatype != nos.datatype) && !(tos.datatype == BOOL && nos.datatype == BOOLEAN))
	{
		/*printf("TOS type different to NOS");*/
		/*printf("TOS %d NOS %d\n",tos.datatype,nos.datatype);*/
		/* tos INT nos FLOAT */
		if(tos.datatype == INT && nos.datatype == FLOAT)
		{
			printf("Warning: conversion from float value to int may lose information\n");
			tos.u.ptr_sym->u.ival = (int)nos.u.dval;
			tos.datatype = INT;		
		}	
		else if(tos.datatype == FLOAT && nos.datatype == INT)
		{
			printf("Information: assignment of int value to a float\n");
			tos.u.ptr_sym->u.dval = (double)nos.u.ival;
			tos.datatype = FLOAT;		
		}
		else if(tos.datatype == CHAR && nos.datatype == INT)
		{
			printf("Information: assignment of int value to a char\n");
			tos.u.ptr_sym->u.cval = (char)nos.u.ival;
			tos.datatype = INT;		
		}
		else if(tos.datatype == BOOL && nos.datatype == INT)
		{
			printf("Information: assignment of int value to a bool\n");
			tos.u.ptr_sym->u.bval = (bool)nos.u.ival;
			tos.datatype = INT;		
		}
		else
		{
			printf("Attempt to assign value of different type to variable not allowed\n");
		}
	}
	else
	{
		/* NOS and TOS types are the same */
		switch(tos.datatype)
		{
		case INT:
			tos.u.ptr_sym->u.ival = nos.u.ival;
			break;
		case CHAR:
			tos.u.ptr_sym->u.cval = nos.u.cval;
			break;
		case BOOL:
			tos.u.ptr_sym->u.bval = nos.u.bval;
			break;		
		case FLOAT:
		default:
			tos.u.ptr_sym->u.dval = nos.u.dval;
			break;
		}
		tos.datatype = nos.datatype;
	}

	/* Variable is no longer undefined as value set to it */	
	tos.u.ptr_sym->itype = VAR;
	push(tos);
}

/* Print TOS */
void print() {
	Datum_t tos;
	tos = pop();
	/* Add type checking */
	switch(tos.datatype)
	{
	case INT:		
		printf("\t%d\n", tos.u.ival);
		break;
	case CHAR:
		printf("\t%c\n",tos.u.cval);
		break;
	case BOOL:
		printf("\t%d\n",tos.u.bval);
		break;
	case FLOAT:
	default:
		printf("\t%.8g\n", tos.u.dval);
		break;
	}
}

/* Print numeric value */
void prexpr() {
	Datum_t tos;
	tos = pop();
	if ( trace_mode ) {
		printstack();
		printf("\n");
		trace_nostack = 0;
	}
	switch(tos.datatype)
	{
	case INT:		
		printf("ival %d ", tos.u.ival);
		break;
	case CHAR:
		printf("cval %c ",tos.u.cval);
		break;
	case BOOL:
		printf("bval %d ",tos.u.bval);
		break;
	case FLOAT:
	default:
		printf("dval %.8g ", tos.u.dval);
		break;
	}
	
}

/* Print a string */
#include <ctype.h>
void prstr() {
	char *ptr_char;

	if ( trace_mode ) {
		ptr_char = (char *) *(ptr_local++);
		printf("\"");
		while ( *ptr_char != '\0' ) {
			if ( iscntrl((int)*ptr_char) ) {
				printf(".");
			} else {
				printf("%c", *ptr_char);
			}
			++ptr_char;
		}
		printf("\"\n");
		trace_nostack = 0;
	} else {
		printf("%s", (char *) *(ptr_local++));
	}
}

/* Read into variable */
void varread() {
	printf("varread\n");
	Datum_t tos;
	extern FILE *fin;	/* provided by hoc.y */
	Symbol_t *ptr_sym;

	ptr_sym = (Symbol_t *)(ptr_local++);

Again:
	switch ( fscanf(fin, "%1f", (float *)&ptr_sym->u.dval)) {
	case EOF:
		if ( nextfile() ) {
			goto Again;
		}
		tos.u.dval = ptr_sym->u.dval = 0.0;
		break;
	case 0:
		execerror("non-number read into", ptr_sym->ptr_cname);
		break;
	default:
		tos.u.dval = 1.0; /* True */
		break;
	}
	ptr_sym->itype = VAR;
	push(tos);
}
